package eclMain;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.List;

import org.joda.time.DateTime;

//do an initial pass to grab any files that have been created within the last 25 hours
//remove any files that have the same listener with the same channel at an earlier date
//that gives me my master list to follow
//start a monitoring thread to find any new files that show up after the program has started
//TODO: each hour, remove files that are older than 25 hours - hasn't been implemented yet

public class FileManager {
	private static ArrayList<ChatFile> masterList = new ArrayList<ChatFile>();
	
	//give it the directory to the eve chat logs
	FileManager(Path directory) throws Exception { //exception thrown if the directory cannot be opened
		ArrayList<ChatFile> fileList = findRecentChannels(directory); //get all relevant files
		for (ChatFile file : fileList) file.readToEoF(); //update all found files to their most recent entry, so I don't get flooded with old chat.
		Notifier.standardNotify("Recent files:");
		for (ChatFile file : fileList) {
			Notifier.standardNotify("Location: " + file.getPath());
			Notifier.standardNotify("Channel Name: " + file.channel.getChannelName());
			Notifier.standardNotify("Listener: " + file.channel.getListener() + "\n");
		}
		Notifier.standardNotify("Total Files Watched: " + fileList.size());
		setMasterList(fileList);
		startWatching(directory);
		Notifier.standardNotify("Listening...");
	}

	/**Finds all recent channels within the last 25 hours. This method should only be called once, when the program starts*/
	private ArrayList<ChatFile> findRecentChannels(Path directory) throws Exception { //exception thrown if the directory cannot be opened
		ArrayList<ChatFile> fileList = new ArrayList<ChatFile>();
		DirectoryStream<Path> stream = Files.newDirectoryStream(directory);
			for (Path file : stream) {
				if (Files.isDirectory(file)) continue; //skip folders/directories
				String fileString = file.toString();
				String extension = fileString.substring(fileString.length()-3);
				if (extension.equalsIgnoreCase("txt")) { //make sure I'm only opening up txt files
					try {
							ChatFile cf = new ChatFile(directory, file);
							if (cf.getChannel().isChannelInitialized() == true) fileList.add(cf); //only add the file if the header information was found
					} catch (Exception e) {
						Notifier.errorNotify("Unable to add file: " + file.toString());
						Notifier.errorNotify("\t" + e.toString());
						//can't do anything about this; simply ignore and go to the next file
						e.printStackTrace();
					}
				}
			}
		
		//remove files older than 25 hours, remove files that are no longer active, return what's left
		return removeInactiveFiles(new ArrayList<ChatFile>(), removeOldFiles(fileList));
	}
	
	/**Removes any files in the list that are older than 25 hours; this may give me a few extra files to track,
	 * but that will be better than missing some if the Eve server for whatever reason doesn't shut down at the normal time */
	private ArrayList<ChatFile> removeOldFiles(ArrayList<ChatFile> fileList) {
		ArrayList<ChatFile> newList = new ArrayList<ChatFile>();
		DateTime yesterday = new DateTime();
		yesterday = yesterday.minusHours(25);
		
		for (ChatFile cFile : fileList) {
			if (cFile.getCreationTime().compareTo(yesterday) > 0) { //if the file was created in the last 25 hours, add it to the list
				newList.add(cFile);
			}
		}
		return newList;
	}
	
	//for all files in newChannels, check to see if there is one with the same name and listener in origList.
	//If there is, determine which is newer and keep that
	private ArrayList<ChatFile> removeInactiveFiles(ArrayList<ChatFile> origList, ArrayList<ChatFile> newChannels) {
		for (int i = 0; i < newChannels.size(); i++) {
			ChatFile newFile = newChannels.get(i);
			
			boolean conflict = false;
			for (int j = 0; j < origList.size(); j++) {
				ChatFile origFile = origList.get(j);
				//if the channel name and listener are the same for both files, determine which is the most recent
				//XXX: makes sure this works well for both normal and complex channels (i.e. corp, local, etc.)
				if (newFile.channel.getChannelName().equals(origFile.channel.getChannelName()) && newFile.channel.getListener().equals(origFile.channel.getListener())) {
					if (newFile.isMoreRecentThan(origFile)) {
						origList.set(j, newFile);
						Notifier.standardNotify("Replacing file \"" + origFile.getPath() + "\" with \"" + newFile.getPath() + "\"");
					}
					conflict = true;
					break;
				}
			}
			
			if (!conflict) origList.add(newFile);
		}
		return origList;
	}
	
	//watches the given directory for new files, and merges them with the master active file list
	private void startWatching(final Path directory) {
		try {
			final WatchService watcher = directory.getFileSystem().newWatchService();
			directory.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
			Thread thread = new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						while (true) {
							WatchKey watchKey = watcher.take(); //waits until there is an update in the directory
							Thread.sleep(500); //wait for half a second, because I'm finding files faster than Eve can write the header into them
							ArrayList<ChatFile> newFiles = new ArrayList<ChatFile>();
							
							List<WatchEvent<?>> events = watchKey.pollEvents(); //get all update events
							for (WatchEvent<?> event : events) {
								if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) { //if it's a new file, add it to the list
									Path filePath = (Path)event.context();
									String fileString = filePath.toString();
									String extension = fileString.substring(fileString.length()-3);
									if (extension.equalsIgnoreCase("txt")) { //make sure I'm only opening up txt files
										try {
												ChatFile cf = new ChatFile(directory, filePath);
												if (cf.getChannel().isChannelInitialized() == true) {
													newFiles.add(cf); //only add the file if the header information was found
													Notifier.standardNotify("Found new file: " + newFiles.get(newFiles.size()-1).getPath());
												}
										} catch (Exception e) {
											Notifier.errorNotify("Unable to add file: " + filePath.toString());
											//can't do anything about this; simply ignore and go to the next file
						//					e.printStackTrace();
										}
									}
								} else if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
									Notifier.errorNotify("Warning: files not tracked because of watchKey overflow: " + event.count());
									//TODO: implement a catch here that reruns the directory to match missing files, if this gets thrown very often
								}
							}
							
							watchKey.reset(); //need to put it back to the "ready" state; this should fix the problem of consecutive new files not registering
							
							//merge new files with the master list, remove files that have newer counterparts
							ArrayList<ChatFile> mergedList = removeInactiveFiles(getMasterList(), newFiles);
							
							//add all found files to the master list
							setMasterList(mergedList);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
			thread.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**Returns the size of the Master List*/
	public synchronized int getListSize() {
		return FileManager.masterList.size();
	}
	
	/**Use this if a files misbehaves*/
	public synchronized void removeFileFromList(ChatFile file) {
		FileManager.masterList.remove(file);
	}
	
	/**This method copies the current master list, and returns the copy. If you want to simply get a count of the array, use getListSize() instead.*/
	public static synchronized ArrayList<ChatFile> getMasterList() {
		return FileManager.masterList;
	}
	
	private synchronized void setMasterList(ArrayList<ChatFile> masterList) {
		FileManager.masterList = masterList;
	}
	
	public static void main(String[] args) throws Exception {
		new FileManager(Paths.get(System.getProperty("user.home"), "Documents", "EVE", "logs", "Chatlogs"));
	}
}
